use std::{
    collections::{BTreeMap, HashSet},
    fs::read_to_string,
};

use serde::{de::MapAccess, Deserialize, Deserializer};

#[derive(Debug)]
struct ProvinceMap(Vec<HashSet<String>>);

impl<'de> Deserialize<'de> for ProvinceMap {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = ProvinceMap;

            fn expecting(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
                Ok(())
            }

            fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
            where
                M: MapAccess<'de>,
            {
                let mut provinces: Vec<HashSet<String>> = Vec::new();

                // For each entry in the map
                while let Some((key, value)) = map.next_entry::<String, Vec<String>>()? {
                    // Create a new set with the key and value
                    let mut set = HashSet::<_>::from_iter(value);
                    set.insert(key);

                    let mut new_provinces = Vec::new();

                    for province in provinces {
                        if province.intersection(&set).next().is_some() {
                            // The set has common elements with the province, merge them
                            set.extend(province);
                        } else {
                            // This province has no common elements with the set, keep it
                            new_provinces.push(province);
                        }
                    }

                    new_provinces.push(set);
                    provinces = new_provinces;
                }

                Ok(ProvinceMap(provinces))
            }
        }

        deserializer.deserialize_map(Visitor)
    }
}

pub fn count_provinces() -> String {
    // Deserialize the JSON from a `String` will benefit from buffering and caching
    let json_content = read_to_string("district.json").unwrap();
    serde_json::from_str::<BTreeMap<String, ProvinceMap>>(&json_content)
        .unwrap()
        .values()
        .map(|v| v.0.len().to_string())
        .collect::<Vec<_>>()
        .join(",")
}
